package fem2.pre_and_post;

import java.util.ArrayList;

import fem2.MeshGenerator;

/**
 * * Creates patches of 2D structureed meshes.
 * 
 * @author hbui
 * 
 */
public class PatchMeshGenerator extends MeshGenerator {

	public enum PatchType {
		QUADRILATERAL, //
		TRIANGLE_LEFT_TO_RIGHT, //
		TRIANGLE_RIGHT_TO_LEFT //
	}

	abstract class FunctionNToR {
		public abstract double valueAt(int n);
	}

	private int nc, nr;
	private FunctionNToR fx, fy;
	private PatchType type = PatchType.QUADRILATERAL;

	/**
	 * create rectangular patch with origin at (x0,y0) and equal distance
	 * (dx,dy)
	 * 
	 * <pre>
	 * nr
	 *     *-----*-----*-----*
	 *     |     |     |     |
	 *     |     |     |     |
	 *     *-----*-----*-----*
	 * j ^ |     |     |     |
	 *   | |     |     |     |
	 *   - *-----*-----*-----*
	 *     |--> i           nc
	 * </pre>
	 * 
	 * @param nc
	 *            number of columns
	 * @param nr
	 *            number of rows
	 * @param x0
	 * @param y0
	 * @param dx
	 * @param dy
	 */
	public PatchMeshGenerator(int nc, int nr, double x0, double y0, double dx, double dy) {
		this.nc = nc;
		this.nr = nr;
		final double x0_ = x0;
		final double y0_ = y0;
		final double dx_ = dx;
		final double dy_ = dy;

		fx = new FunctionNToR() {

			@Override
			public double valueAt(int n) {
				return x0_ + n * dx_;
			}

		};

		fy = new FunctionNToR() {

			@Override
			public double valueAt(int n) {
				return y0_ + n * dy_;
			}

		};
	}

	/**
	 * set the type for this patch
	 * 
	 * @param type
	 */
	public void setType(PatchType type) {
		this.type = type;
	}

	/**
	 * 
	 * @param i
	 * @param j
	 * @return coodinates at point (i,j)
	 */
	private double[] getCoordinates(int i, int j) {
		return new double[] { fx.valueAt(i), fy.valueAt(j) };
	}

	@Override
	public ArrayList<double[]> getNodes(int dim) {
		ArrayList<double[]> nodes = new ArrayList<double[]>();
		for (int i = 0; i < nr; i++) {
			for (int j = 0; j < nc; j++) {
				double[] n = getCoordinates(j, i);
				nodes.add(n);
			}
		}
		return nodes;
	}

	@Override
	public ArrayList<ArrayList<Integer>> getConnectivities() {
		ArrayList<ArrayList<Integer>> con = new ArrayList<ArrayList<Integer>>();
		for (int i = 0; i < nr - 1; i++) {
			for (int j = 0; j < nc - 1; j++) {
				int n1 = i * nc + j;
				int n2 = i * nc + j + 1;
				int n3 = (i + 1) * nc + j + 1;
				int n4 = (i + 1) * nc + j;
				switch (type) {
				case QUADRILATERAL:
					ArrayList<Integer> e = new ArrayList<Integer>();
					e.add(n1);
					e.add(n2);
					e.add(n3);
					e.add(n4);
					con.add(e);
					break;
				case TRIANGLE_LEFT_TO_RIGHT:
					ArrayList<Integer> e1 = new ArrayList<Integer>();
					ArrayList<Integer> e2 = new ArrayList<Integer>();
					e1.add(n1);
					e1.add(n2);
					e1.add(n3);
					e2.add(n1);
					e2.add(n3);
					e2.add(n4);
					con.add(e1);
					con.add(e2);
					break;
				case TRIANGLE_RIGHT_TO_LEFT:
					ArrayList<Integer> e3 = new ArrayList<Integer>();
					ArrayList<Integer> e4 = new ArrayList<Integer>();
					e3.add(n1);
					e3.add(n2);
					e3.add(n4);
					e4.add(n2);
					e4.add(n3);
					e4.add(n4);
					con.add(e3);
					con.add(e4);
					break;
				default:
					throw new Error("invalid patch type");
				}
			}
		}
		return con;
	}

}
